/*
 * @Description: 
 * @FilePath: /STM32F103/USER/bsp/iic.c
 * @Author: Wei Zhou
 * @Github: https://github.com/zromyk
 * @Date: 2021-05-12 21:29:24
 * @LastEditors: Wei Zhou
 * @LastEditTime: 2021-05-13 00:19:43
 * @Copyright: Copyright © 2017 muyiro. All rights reserved.
 */
#include "iic.h"
#include "delay.h"

void IIC_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}

static void iic_delay(void)
{
    delay_us(4);
}

/* 产生IIC起始信号 */
static void iic_start(void)
{
    SDA_OUT();      /* sda线输出 */
    iic_delay();
    IIC_SDA = 1;
    IIC_SCL = 1;
    iic_delay();
    IIC_SDA = 0;    /* START：when CLK is high,DATA change form high to low */
    iic_delay();
    IIC_SCL = 0;    /* 钳住I2C总线，准备发送或接收数据 */
}

/* 产生IIC停止信号 */
static void iic_stop(void)
{
    SDA_OUT();      /* sda线输出 */
    iic_delay();
    IIC_SCL = 0;
    IIC_SDA = 0;    /* STOP：when CLK is high DATA change form low to high */
    iic_delay();
    IIC_SCL = 1;
    iic_delay();
    IIC_SDA = 1;    /* 发送I2C总线结束信号 */
    iic_delay();
}

/* 等待应答信号到来 */
/* 返回值：1，接收应答失败 */
/*        0，接收应答成功 */
static uint8_t iic_wait_ack(void)
{
    uint8_t ucErrTime = 0;

    SDA_IN();       /* SDA设置为输入 */
    IIC_SCL = 1;
    iic_delay();
    while (READ_SDA)
    {
        ucErrTime++;
        if (ucErrTime > 250)
        {
            iic_stop();
            return 1;
        }
    }
    IIC_SCL = 0;    /* 时钟输出0 */
    iic_delay();
    return 0;
}

/* 产生ACK应答 */
static void iic_ack(void)
{
    IIC_SCL = 0;
    SDA_OUT();
    IIC_SDA = 0;
    iic_delay();
    IIC_SCL = 1;
    iic_delay();
    IIC_SCL = 0;
}

/* 不产生ACK应答 */
static void iic_nack(void)
{
    IIC_SCL = 0;
    SDA_OUT();
    IIC_SDA = 1;
    iic_delay();
    IIC_SCL = 1;
    iic_delay();
    IIC_SCL = 0;
}

/* IIC发送一个字节 */
/* 返回从机有无应答 */
/* 1，有应答 */
/* 0，无应答 */
static void iic_send_byte(uint8_t tx)
{
    uint8_t t;
    SDA_OUT();
    IIC_SCL = 0; /* 拉低时钟开始数据传输 */
    for (t = 0; t < 8; t++)
    {
        if ((tx & 0x80) >> 7)
            IIC_SDA = 1;
        else
            IIC_SDA = 0;
        tx <<= 1;
        /* iic_delay(); /* 对TEA5767这三个延时都是必须的 */
        IIC_SCL = 1;
        iic_delay();
        IIC_SCL = 0;
        iic_delay();
    }
}

/* 读1个字节，ack=1时，发送ACK，ack=0，发送nACK */
static uint8_t iic_read_byte(uint8_t ack)
{
    uint8_t i, receive = 0;

    SDA_IN();           /* SDA设置为输入 */
    for (i = 0; i < 8; i++)
    {
        IIC_SCL = 0;
        iic_delay();
        IIC_SCL = 1;
        receive <<= 1;
        if (READ_SDA)
            receive++;
        iic_delay();
    }
    if (!ack)
        iic_nack();     /* 发送nACK */
    else
        iic_ack();      /* 发送ACK */
    return receive;
}

/* 向I2C设备寄存器写入一个字节数据 */
uint8_t IIC_Write_Reg(uint8_t dev, uint8_t reg, uint8_t data)
{
    uint8_t ack = 0x00;

    iic_start();                /* 发送起始信号 */
    iic_send_byte(dev);         /* 发送设备地址+写信号 */
    ack |= iic_wait_ack();
    iic_send_byte(reg);         /* 内部寄存器地址 */
    ack |= iic_wait_ack();
    iic_send_byte(data);        /* 内部寄存器数据 */
    ack |= iic_wait_ack();
    iic_stop();                 /* 发送停止信号 */

    return ack;
}

/* 向I2C设备寄存器读取一个字节数据 */
uint8_t IIC_Read_Reg(uint8_t dev, uint8_t reg, uint8_t *data)
{
    uint8_t ack = 0x00;

    iic_start();                /* 发送起始信号 */
    iic_send_byte(dev);         /* 发送设备地址+写信号 */
    ack |= iic_wait_ack();
    iic_send_byte(reg);         /* 发送存储单元地址，从0开始 */
    ack |= iic_wait_ack();
    iic_start();                /* 发送起始信号 */
    iic_send_byte(dev + 1);     /* 发送设备地址+读信号 */
    ack |= iic_wait_ack();
    *data = iic_read_byte(0);   /* 读出寄存器数据 0 NACK 1 ACK */
    iic_stop();                 /* 发送停止信号 */

    return ack;
}

/* 向I2C设备寄存器读取2个字节数据 */
uint8_t IIC_Read_Word(uint8_t dev, uint8_t reg, uint16_t *data)
{
    uint8_t ack = 0x00;
    
    iic_start();                    /* 发送起始信号 */
    iic_send_byte(dev);             /* 发送设备地址+写信号 */
    ack |= iic_wait_ack();
    iic_send_byte(reg);             /* 内部寄存器地址 */
    ack |= iic_wait_ack();
    iic_start();                    /* 发送起始信号 */
    iic_send_byte(dev + 1);         /* 发送设备地址+读信号 */
    ack |= iic_wait_ack();
    
    *data = iic_read_byte(1);       /* 读出寄存器数据 0 NACK 1 ACK */
    *data = ((*data) << 8) | iic_read_byte(0);       /* 读出寄存器数据 0 NACK 1 ACK */
    iic_stop();                     /* 发送停止信号 */

    return ack;
}

/* 向I2C设备寄存器读取多个字节数据 */
/* dev_add  设备地址(低七位地址) */
/* reg      寄存器地址 */
/* data     数据保存的地址指针 */
/* len      读取字节数量 */
uint8_t IIC_Read_Regs(uint8_t dev, uint8_t reg, uint8_t *data, uint8_t len)
{
    uint8_t ack = 0x00;

    iic_start();                    /* 发送起始信号 */
    iic_send_byte(dev);             /* 发送设备地址+写信号 */
    ack |= iic_wait_ack();
    iic_send_byte(reg);             /* 内部寄存器地址 */
    ack |= iic_wait_ack();
    iic_start();                    /* 发送起始信号 */
    iic_send_byte(dev + 1);         /* 发送设备地址+读信号 */
    ack |= iic_wait_ack();
    while (--len)
    {
        *data = iic_read_byte(1);   /* 读出寄存器数据 0 NACK 1 ACK */
        data++;
    }
    *data = iic_read_byte(0);       /* 读出寄存器数据 0 NACK 1 ACK */
    iic_stop();                     /* 发送停止信号 */

    return ack;
}
